// $Id: ast_bodybuilder.c 29 2015-03-11 03:49:12Z robim.poa@gmail.com $
//-----------------------------------------------------------------------------
// Purpose: Scans code body parts and build AST tree.
// $Rev: 29 $
// $Author: robim.poa@gmail.com $
// $Date: 2015-03-11 03:49:12 +0000 (Wed, 11 Mar 2015) $

#include <stdlib.h>
#include <stdio.h>

#include "debug.h"
#include "ast_bodybuilder.h"
#include "text_processing.h"
#include "ast.h"
#include "expression.h"


struct node_ast* Scan(FILE *pfile, struct tp_node_var* nodep_var_head)
{
	struct node_ast *node_ast_head = astCreateNode(AST_BODY, 0);

	if(tdEndOfFile(pfile))
		return NULL;

	node_ast_head->left = GetInstruction(pfile, nodep_var_head);

	node_ast_head->right = Scan(pfile, nodep_var_head);

	return node_ast_head;
}

struct node_ast* ScanBody(FILE *pfile, struct tp_node_var* nodep_var_head)
{
	struct node_ast *node_ast_head = astCreateNode(AST_BODY, 0);

	if(tdLookNextChar(pfile)=='}')
	{
		tdGetNonBlankChar(pfile);
		return NULL;
	}

	node_ast_head->left = GetInstruction(pfile, nodep_var_head);

	node_ast_head->right = ScanBody(pfile, nodep_var_head);

	return node_ast_head;
}

struct node_ast* GetInstruction(FILE *pfile, struct tp_node_var* nodep_var_head)
{	
	char buffer[255];

	tdLookNextString(buffer, pfile);

	switch(tpStringToStatement(buffer))
	{
		case STATEMENT_WHILE:
			tdGetString(buffer, pfile);
			return bbStatementWhile(pfile, nodep_var_head);
			break;

		case STATEMENT_IF:
			tdGetString(buffer, pfile);
			return bbStatementIf(pfile, nodep_var_head);
			break;

		case STATEMENT_FOR:
			tdGetString(buffer, pfile);
			return bbStatementFor(pfile, nodep_var_head);
			break;

		case STATEMENT_SWITCH:
			tdGetString(buffer, pfile);
			break;

		case STATEMENT_TYPE_INT:
			tdGetString(buffer, pfile);
			stDeclaration(pfile, nodep_var_head, STATEMENT_TYPE_INT);
			return GetInstruction(pfile, nodep_var_head);
			break;

		case STATEMENT_TYPE_FLOAT:
			tdGetString(buffer, pfile);
			stDeclaration(pfile, nodep_var_head, STATEMENT_TYPE_FLOAT);
			return GetInstruction(pfile, nodep_var_head);
			break;

		case STATEMENT_TYPE_CHAR:
			tdGetString(buffer, pfile);
			stDeclaration(pfile, nodep_var_head, STATEMENT_TYPE_CHAR);
			return GetInstruction(pfile, nodep_var_head);
			break;

		case STATEMENT_TYPE_DOUBLE:
			tdGetString(buffer, pfile);
			stDeclaration(pfile, nodep_var_head, STATEMENT_TYPE_DOUBLE);
			return GetInstruction(pfile, nodep_var_head);
			break;

		default:
			if(tdLookNextChar(pfile)=='(')
			{
				tdGetString(buffer, pfile);
				printf("Function: %s\n",buffer);
			}
			else if(tdLookNextChar(pfile)=='{')
			{
				tdGetNonBlankChar(pfile);
				return ScanBody(pfile, nodep_var_head);
			}
			else if(tdLookNextChar(pfile)=='}')
			{
				tdGetNonBlankChar(pfile);
				return NULL;
			}
			else
			{
				return stExpression(pfile, nodep_var_head, EXPRESSION_END_SEMICOLON);
			}
	}

	printf("Critical error: GetInstruction\n");

	return NULL;
}


void stDeclaration(FILE *pfile, struct tp_node_var* nodep_var_head, int statement)
{
			char buffer[255];

			tdGetString(buffer,pfile);
			if(tpCheckVarExistance(nodep_var_head, buffer)<0)
			{
				switch(statement)
				{
					case STATEMENT_TYPE_CHAR:
						#ifdef DEBUG
							printf("Declaration: \"%s\" as char\n",buffer);
						#endif
						tpCreateVarNode(nodep_var_head, buffer, STATEMENT_TYPE_CHAR);
						break;

					case STATEMENT_TYPE_DOUBLE:
						#ifdef DEBUG
							printf("Declaration: \"%s\" as double\n",buffer);
						#endif
						tpCreateVarNode(nodep_var_head, buffer, STATEMENT_TYPE_DOUBLE);
						break;

					case STATEMENT_TYPE_FLOAT:
						#ifdef DEBUG
							printf("Declaration: \"%s\" as float\n",buffer);
						#endif
						tpCreateVarNode(nodep_var_head, buffer, STATEMENT_TYPE_FLOAT);
						break;

					case STATEMENT_TYPE_INT:
						#ifdef DEBUG
							printf("Declaration: \"%s\" as int\n",buffer);
						#endif
						tpCreateVarNode(nodep_var_head, buffer, STATEMENT_TYPE_INT);
						break;

					default:
						printf("Critical error (STATE_DECLARATION)\n");
						// return 1;
				}
			}
			else
			{
				printf("%d:error: redeclaration of '%s'\n", tdGetLine(pfile), buffer);
			}

			buffer[0] = tdGetNonBlankChar(pfile);
			if(buffer[0]==';')
			{
				// printf("aaa");
			}
			else if(buffer[0]==',')
			{
				// state = STATE_DECLARATION;
				stDeclaration(pfile, nodep_var_head, statement);
			}
			else
			{
				printf("%d:error: expected ';' before '%c'\n", tdGetLine(pfile), buffer[0]);
			}
}

struct node_ast* stExpression(FILE* pfile, struct tp_node_var* nodep_var_head, int expression_kind)
{
	struct tp_node_expression *node_expression_head = (struct tp_node_expression*) malloc(sizeof(struct tp_node_expression));
	node_expression_head->next = NULL;

	tpReadExpression(node_expression_head, expression_kind, nodep_var_head, pfile);

#ifdef DEBUG
	PrintExpressionArray(node_expression_head);
#endif

	return BuildExpressionTree(node_expression_head->next);
}

void PrintVars(struct tp_node_var* nodep_var_head)
{
	while(nodep_var_head != NULL)
	{
		printf("[%2d] %s \t-> %d\n", nodep_var_head->id, nodep_var_head->name, nodep_var_head->type);
		nodep_var_head = nodep_var_head->next;
	}
}

struct node_ast* bbStatementIf(FILE *pfile, struct tp_node_var* nodep_var_head)
{
	if(tdGetNonBlankChar(pfile) != '(')
	{
		printf("%d:error:expected \"(\"", tdGetLine(pfile));
	}

	struct node_ast *node_ast_aux, *node_ast_head;
	char buffer[255];

	node_ast_head        = astCreateNode(AST_IF, 0);

	// Cria node à esquerada do ast_if com expressão comparadora
	node_ast_head->left  = stExpression(pfile, nodep_var_head, EXPRESSION_END_PARENTHESES);


	// Cria ast_else à direita do ast_if
	node_ast_aux         = astCreateNode(AST_ELSE, 0);
	node_ast_head->right = node_ast_aux;

	// Escaneia e constroi body do if
	node_ast_aux->left = GetInstruction(pfile, nodep_var_head);

	// Verifica se existe else
	tdLookNextString(buffer, pfile);
	if(tpStringToStatement(buffer) == STATEMENT_ELSE)
	{
		tdGetString(buffer, pfile);

		// Escaneia e constroi body do else
		node_ast_aux->right = GetInstruction(pfile, nodep_var_head);
	}

	return node_ast_head;
}

struct node_ast* bbStatementWhile(FILE *pfile, struct tp_node_var* nodep_var_head)
{
	if(tdGetNonBlankChar(pfile) != '(')
	{
		printf("%d:error:expected \"(\"", tdGetLine(pfile));
	}

	struct node_ast *node_ast_head = astCreateNode(AST_WHILE, 0);

	// Cria node à esquerada do ast_while com expressão comparadora
	node_ast_head->left  = stExpression(pfile, nodep_var_head, EXPRESSION_END_PARENTHESES);

	// Escaneia body do while
	node_ast_head->right = GetInstruction(pfile, nodep_var_head);

	return node_ast_head;
}

struct node_ast* bbStatementFor(FILE *pfile, struct tp_node_var* nodep_var_head)
{
	if(tdGetNonBlankChar(pfile) != '(')
	{
		printf("%d:error:expected \"(\"", tdGetLine(pfile));
	}

	struct node_ast *node_ast_aux, *node_ast_head = astCreateNode(AST_FOR, 0);

	node_ast_head->left  = astCreateNode(AST_BODY, 0);
 	node_ast_aux         = node_ast_head->left;

 	node_ast_aux->left   = stExpression(pfile, nodep_var_head, EXPRESSION_END_SEMICOLON);
 	node_ast_aux->right  = astCreateNode(AST_BODY, 0);
 	node_ast_aux         = node_ast_aux->right;

 	node_ast_aux->left   = stExpression(pfile, nodep_var_head, EXPRESSION_END_SEMICOLON);
 	node_ast_aux->right  = astCreateNode(AST_BODY, 0);
 	node_ast_aux         = node_ast_aux->right;

	node_ast_aux->left   = stExpression(pfile, nodep_var_head, EXPRESSION_END_PARENTHESES);

	node_ast_head->right = GetInstruction(pfile, nodep_var_head);
	return node_ast_head;
}